﻿/*************************************************************************************
Copyright (c) 2007 Javier Lozano, http://blog.lozanotek.com

Permission is hereby granted, free of charge, to any person obtaining 
a copy of this software and associated documentation files (the "Software"), 
to deal in the Software without restriction, including without limitation 
the rights to use, copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software, and to permit persons to whom the 
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included 
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*************************************************************************************/

namespace flickrNSurface.SilverlightSurface
{
    using System;
    using System.IO;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Browser;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Interop;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Collections.Generic;

    internal enum ActionType { Selecting, Moving, RotatingScaling };

    public partial class Page : Canvas
    {
        private Photo _activePhoto;
        private Photo _pendingPhoto;
        private ActionType _actionType;
        private Point _photoCenter;
        private Point _lastPosition;
        private bool _photosLoaded;

        public void Page_Loaded(object o, EventArgs e)
        {
            // Hook up event handlers
            BrowserHost.Resize += new EventHandler(BrowserHost_Resize);
            MouseLeftButtonUp += new MouseEventHandler(Page_MouseLeftButtonUpOrLeave);
            MouseLeave += new EventHandler(Page_MouseLeftButtonUpOrLeave);
            MouseMove += new MouseEventHandler(Page_MouseMove);
        }

        private void BrowserHost_Resize(object sender, EventArgs e)
        {
            // Set size to host size
            Width = BrowserHost.ActualWidth;
            Height = BrowserHost.ActualHeight;
            if (!_photosLoaded && (0 != Width) && (0 != Height))
            {
                // If photos haven't been loaded yet and the control is initialized, load them now
                var urls = GetImageUrls();

                if (urls != null)
                {
                    foreach (var file in urls)
                    {
                        new Photo(this, new Uri(file));
                    }

                    _photosLoaded = true;
                }
            }
        }

        private static IList<string> GetImageUrls()
        {
            string setId = HtmlPage.QueryString["setId"];
            ImageUrlService svc = new ImageUrlService();

            return svc.GetImageEndpoints(setId);
        }

        private void Page_MouseLeftButtonUpOrLeave(object sender, EventArgs e)
        {
            if (null != _activePhoto)
            {
                // Tell the previously active photo to hide its controls (in case the mouse already left it)
                var previouslyActivePhoto = _activePhoto;
                _activePhoto = null;
                previouslyActivePhoto.HideControls();
            }
            if (null != _pendingPhoto)
            {
                // Tell the pending photo (if any) to show its controls
                _pendingPhoto.ShowControls();
                _pendingPhoto = null;
            }
        }

        private void Page_MouseMove(object sender, MouseEventArgs e)
        {
            if (null != _activePhoto)
            {
                // Perform the appropriate transform on the active photo
                var position = e.GetPosition(null);
                switch (_actionType)
                {
                    case ActionType.Moving:
                        // Move it by the amount of the mouse move
                        _activePhoto.Translate(position.X - _lastPosition.X, position.Y - _lastPosition.Y);
                        break;
                    case ActionType.RotatingScaling:
                        // Rotate it according to the angle the mouse moved around the photo's center
                        var radiansToDegrees = 360 / (2 * Math.PI);
                        var lastAngle = Math.Atan2(_lastPosition.Y - _photoCenter.Y, _lastPosition.X - _photoCenter.X) * radiansToDegrees;
                        var currentAngle = Math.Atan2(position.Y - _photoCenter.Y, position.X - _photoCenter.X) * radiansToDegrees;
                        _activePhoto.Rotate(currentAngle - lastAngle);

                        // Scale it according to the distance the mouse moved relative to the photo's center
                        var lastLength = Math.Sqrt(Math.Pow(_lastPosition.Y - _photoCenter.Y, 2) + Math.Pow(_lastPosition.X - _photoCenter.X, 2));
                        var currentLength = Math.Sqrt(Math.Pow(position.Y - _photoCenter.Y, 2) + Math.Pow(position.X - _photoCenter.X, 2));
                        _activePhoto.Scale(currentLength / lastLength);
                        break;
                }
                _lastPosition = position;
            }
        }

        internal void SetActivePhoto(Photo photo, ActionType actionType, Point photoCenter, Point lastPosition)
        {
            if (null == _activePhoto)
            {
                // Set the active photo and note the relevant details
                _activePhoto = photo;
                _actionType = actionType;
                _photoCenter = photoCenter;
                _lastPosition = lastPosition;

                // Bring the active photo to the top
                Children.Remove(_activePhoto);
                Children.Add(photo);
            }
        }

        internal Photo GetActivePhoto()
        {
            return _activePhoto;
        }

        internal void SetPendingPhoto(Photo photo)
        {
            _pendingPhoto = photo;
        }
    }

    internal class Photo : Control
    {
        private static Random random = new Random();

        private Page _parent;
        private FrameworkElement _root;
        private Image _image;
        private Canvas _allControls;
        private RotateTransform _rotateTransform;
        private ScaleTransform _scaleTransform;
        private TranslateTransform _translateTransform;
        private bool _mouseOver;

        public Photo(Page parent, Uri photo)
        {
            // Load the control's XAML from the embedded resource
            string resourceName = "flickrNSurface.SilverlightSurface.Photo.xaml";
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    _root = InitializeFromXaml(reader.ReadToEnd());
                }
            }

            // Fetch elements from the loaded XAML
            _image = _root.FindName("image") as Image;
            _rotateTransform = _root.FindName("rotateTransform") as RotateTransform;
            _scaleTransform = _root.FindName("scaleTransform") as ScaleTransform;
            _translateTransform = _root.FindName("translateTransform") as TranslateTransform;
            _allControls = _root.FindName("allControls") as Canvas;
            var translateControls = _root.FindName("translateControls") as Canvas;
            var rotateScaleControls = _root.FindName("rotateScaleControls") as Canvas;

            // Initialize
            _parent = parent;
            _allControls.Opacity = 0;

            // Hook up event handlers
            _root.MouseLeftButtonDown += new MouseEventHandler(_root_MouseLeftButtonDown);
            _allControls.MouseEnter += new MouseEventHandler(_allControls_MouseEnter);
            _allControls.MouseLeave += new EventHandler(_allControls_MouseLeave);
            translateControls.MouseLeftButtonDown += new MouseEventHandler(translateControls_MouseLeftButtonDown);
            rotateScaleControls.MouseLeftButtonDown += new MouseEventHandler(rotateScaleControls_MouseLeftButtonDown);

            // Create a Downloader to fetch the image
            var downloader = new Downloader();
            downloader.Completed += new EventHandler(downloader_Completed);
            downloader.DownloadFailed += new ErrorEventHandler(downloader_DownloadFailed);
            downloader.Open("GET", photo);
            downloader.Send();
        }

        void _allControls_MouseEnter(object sender, MouseEventArgs e)
        {
            _mouseOver = true;
            ShowControls();
        }

        void _allControls_MouseLeave(object sender, EventArgs e)
        {
            _mouseOver = false;
            HideControls();
        }

        void _root_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            HandleMouseLeftButtonDown(ActionType.Selecting, e);
        }

        void translateControls_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            HandleMouseLeftButtonDown(ActionType.Moving, e);
        }

        void rotateScaleControls_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {
            HandleMouseLeftButtonDown(ActionType.RotatingScaling, e);
        }

        void HandleMouseLeftButtonDown(ActionType actionType, MouseEventArgs e)
        {
            // Give the parent information about the new active photo
            _parent.SetActivePhoto(this, actionType, new Point(_translateTransform.X + _rotateTransform.CenterX, _translateTransform.Y + _rotateTransform.CenterY), e.GetPosition(null));
        }

        private void downloader_Completed(object sender, EventArgs e)
        {
            var downloader = sender as Downloader;

            // Set the graphic into the Image element
            _image.SetSource(downloader, null);

            // Position and rotate the photo randomly
            Translate(random.Next((int)(BrowserHost.ActualWidth - _root.Width)), random.Next((int)(BrowserHost.ActualHeight - _root.Height)));
            Rotate(random.Next(-30, 30));

            // Add the photo to the parent and play the display animation
            _parent.Children.Add(this);
            (_root.FindName("display") as Storyboard).Begin();
        }

        void downloader_DownloadFailed(object sender, ErrorEventArgs e)
        {
            // Handled only to suppress the error dialog otherwise
        }

        public void Translate(double deltaX, double deltaY)
        {
            _translateTransform.X += deltaX;
            _translateTransform.Y += deltaY;
        }

        public void Rotate(double deltaAngle)
        {
            _rotateTransform.Angle += deltaAngle;
        }

        public void Scale(double deltaScale)
        {
            _scaleTransform.ScaleX *= deltaScale;
            _scaleTransform.ScaleY *= deltaScale;
        }

        public void ShowControls()
        {
            if (_mouseOver)
            {
                // If the mouse is (still) over the photo
                if (null == _parent.GetActivePhoto())
                {
                    // No active photo, so this photo can show its controls
                    _allControls.Opacity = 0.4;
                }
                else
                {
                    // Let the parent know this photo is ready to show its controls
                    _parent.SetPendingPhoto(this);
                }
            }
        }

        public void HideControls()
        {
            if (!_mouseOver && (this != _parent.GetActivePhoto()))
            {
                // If the mouse is not over the photo and it's not the active photo, hide its controls
                _allControls.Opacity = 0;
            }
        }
    }
}
